#ifndef __CXXCALL_TYPEDESC__
#define __CXXCALL_TYPEDESC__   1

#include "abi.hpp"
#include <cstdint>
#include <cstddef>

namespace cxxcall
{

struct type
{
  enum
    {
      void_t,
      schar_t,
      uchar_t,
      sshort_t,
      ushort_t,
      sint_t,
      uint_t,
      slong_t,
      ulong_t,
      sllong_t,
      ullong_t,
      float_t,
      double_t,
      ldouble_t,
      struct_t
    };
};

inline constexpr size_t
stack_slots (size_t align, size_t size)
{
  return ((align > size ? align : size) / sizeof (uintptr_t) + 1);
}

template <class T>
struct typedesc
{
  static const int code = type::struct_t;
  static const unsigned int nslots = stack_slots (alignof (T), sizeof (T));
  typedef T rtype;
};

template <class T>
struct typedesc<T *>
{
#if defined (_WIN32) && defined (CXXCALL_WIDE)
  static const int code = type::ullong_t;
#else
  static const int code = type::ulong_t;
#endif
  static const unsigned int nslots = 1;
  typedef void* rtype;
};

template <>
struct typedesc<void>
{
  static const int code = type::void_t;
  static const unsigned int nslots = 0;
  typedef char rtype;
};

#define TYPEDESC(Type, Code)   \
template <>   \
struct typedesc<Type>   \
{   \
  static const int code = type::Code;   \
  static const unsigned int nslots =   \
    stack_slots (alignof (Type), sizeof (Type));   \
  typedef Type rtype;   \
}

TYPEDESC (signed char, schar_t);
TYPEDESC (unsigned char, uchar_t);
TYPEDESC (short, sshort_t);
TYPEDESC (unsigned short, ushort_t);
TYPEDESC (int, sint_t);
TYPEDESC (unsigned int, uint_t);
TYPEDESC (long, slong_t);
TYPEDESC (unsigned long, ulong_t);
TYPEDESC (long long, sllong_t);
TYPEDESC (unsigned long long, ullong_t);
TYPEDESC (float, float_t);
TYPEDESC (double, double_t);
TYPEDESC (long double, ldouble_t);

#undef TYPEDESC

} // namespace cxxcall

#endif
